最重要的當然是Web Assembly的官網:http://webassembly.org/
裡面有比較好閱讀的文件:http://webassembly.org/docs/high-level-goals/
也有比較難閱讀的規格書:https://webassembly.github.io/spec/(可以看到有Web Assembly本身的規格,以及跟Javascript串接會使用到的物件與API)
另外,MDN的文件也是不錯的參考來源:https://developer.mozilla.org/en-US/docs/WebAssembly
MDN的文件底下有MDN做的範例,拿來學習還蠻容易進入狀況:https://github.com/mdn/webassembly-examples/
目前參考到的文件大概就這些。
Web Assembly是從asm.js進一步發展出來的,提到asm.js,不能不用的工具就是:emscripten,不過我的練習主要集中在直接用Web Assembly的文字格式來撰寫程式,所以不太會用到。現在emscripten除了支援asm.js,也支援輸出成Web Assembly。
目前主要會用到的是:wabt(Web Assembly Binary Toolkit),用他可以把文字格式的wat檔編譯成Binary格式的wasm檔,也可以反過來,把Binary格式轉成文字格式。
要真的用Web Assembly寫Hello World反而有點複雜,因為只有四種型別,就是32位元以及64位元的整數以及浮點數。先用emscripten做一個來看看...首先寫一個hello.c
:
#include <stdio.h>
int main(int argc, char ** argv) {
printf("Hello, world!\n");
}
用emscripten把他編譯成Web Assembly後,看到個檔案:
放到伺服器上打開:
嗯...看起來會在Canvs跟Console都輸出:Hello, world
...不過呢,編譯出來的hello.wasm檔案有44,481 bytes,再把他用wabt工具轉成hello.wat更有513,509 bytes。這樣很難用來學習呢XD
所以先改成Hello add,其實就是寫一個最簡單的add函數做整數加法。
myadd.wat
(module
(func (export "add") (param $a i32) (param $b i32) (result i32)
get_local $a
get_local $b
i32.add))
這個Web Assembly會輸出一個函數給Javascript環境使用。
編譯成myadd.wasm之後,放到html跑跑看(我用MDN的範例改了一下,其實上面程式碼跟範例裡面有一個add.wat也差不多。執行也是呼叫範例裡面包裝好WebAssembly API的函數),就是做3+4
的簡單整數加法:
myadd.html
<html>
<body>
<script src="../wasm-utils.js"></script>
<script>
fetchAndInstantiate('myadd.wasm')
.then(instance => {
console.log(instance.exports.add(3,4));
});
</script>
</body>
</html>
跑起來的畫面:
之前在看規格書中關於Binary Format的部份,發現很難看懂。不過今天看了一下Binary Encoding文件,竟然就對上了...所以來個bonus...
範例是上面的myadd.wasm,內容用Hex String表示是長這樣:
0061736d0100000001070160027f7f017f030201000707010361646400000a09010700200020016a0b
文件閱讀的順序大約是:
\0asm
,這是檔頭的magic number,第二個uint32是版本號01000000
,在wasm中數字編碼都是用Little Endian,所以這裡的版本號就是1
。上面這個wasm的內容,依照這個規則可以分析出來他的結構大概像這樣:
第一欄是資料的定義,第二欄是資料的內容(Hex String),定義前的indent,代表是前面資料內容的更詳細分析:
magic number 0061736d
version 01000000
type_section 01
payload_len 07
payload 0160027f7f017f
count 01
entry[0]:
func 60
param_count 02
param_types 7f7f
i32 7f
i32 7f
return_count 01
return_type 7f
i32 7f
function_section 03
payload_len 02
payload 0100
count 01
type 00
export_section 07
payload_len 07
payload 01036164640000
count 01
entry[0]:
field_length 03
field_string 616464 ;"add"
kind 00
index 00
code_section 0a
payload_len 09
payload 010700200020016a0b
count 01
func_body[0]:
body_size 07
local_count 00
code 200020016a
i32.getlocal 20
param[0] 00
i32.getlocal 20
param[1] 01
i32.add 6a
func_end 0b
簡單地說,wasm的結構就是由一個一個section構成的。本來還怕LEB128不好讀,但是因為程式很短,所有的長度都用一個byte就可以表示,所以就不用另外寫程式來算出解碼後的數字,直接靠工人智慧...
之後大概不會再碰Binary格式了,都是用文字格式來寫。所以明天開始就先來了解一下文字格式,順便看看wabt的使用。
有點看不太懂,不是直接改 hello.c 就好了
為什麼需要去動 wasm 呢
哈哈,因為只是想寫wasm跟熟悉他,不是要熟悉c。